﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.UI.HtmlControls;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Diagnostics;
using System.Reflection;
using System.ComponentModel;
using ICeFramework.Common.Attributes;
using System.Collections.ObjectModel;
using System.IO;
using ICeFramework.Web.Common.Attributes;

namespace ICeFramework.Web.Common
{
    public static class HtmlUtil
    {
        private static readonly Dictionary<Type, IList<string>> _cssCache = new Dictionary<Type, IList<string>>();
        private static readonly object _sync = new object();
        private static readonly bool _isMinifierEnabled = true;

        #region public static HtmlTableRow AddRow(HtmlTable table, params string[] texts)
        /// <summary>
        /// 
        /// </summary>
        /// <param name="table"></param>
        /// <param name="texts"></param>
        /// <returns></returns>
        public static HtmlTableRow AddRow(HtmlTable table, params string[] texts)
        {
            if (table == null)
            {
                table = new HtmlTable();
            }
            return InsertRow(table.Rows.Count, table, texts);
        }
        #endregion

        #region public static HtmlTableRow InsertRow(int index, HtmlTable table, params string[] texts)
        /// <summary>
        /// 
        /// </summary>
        /// <param name="index"></param>
        /// <param name="table"></param>
        /// <param name="texts"></param>
        /// <returns></returns>
        public static HtmlTableRow InsertRow(int index, HtmlTable table, params string[] texts)
        {
            if (table == null)
            {
                table = new HtmlTable();
            }
            HtmlTableRow row = InsertRow(index, table);
            foreach (string str in texts)
            {
                AddCell(row).InnerHtml = str;
            }
            return row;
        }
        #endregion

        #region public static HtmlTableRow InsertRow(int index, HtmlTable table)
        /// <summary>
        /// 
        /// </summary>
        /// <param name="index"></param>
        /// <param name="table"></param>
        /// <returns></returns>
        public static HtmlTableRow InsertRow(int index, HtmlTable table)
        {
            if (table == null)
            {
                table = new HtmlTable();
            }
            HtmlTableRow row = new HtmlTableRow();
            table.Rows.Insert(index, row);
            return row;
        }
        #endregion

        #region public static HtmlTableCell AddCell(HtmlTableRow row)
        /// <summary>
        /// 
        /// </summary>
        /// <param name="row"></param>
        /// <returns></returns>
        public static HtmlTableCell AddCell(HtmlTableRow row)
        {
            HtmlTableCell cell = new HtmlTableCell();
            row.Cells.Add(cell);
            return cell;
        }
        #endregion

        #region public static bool RegisterResource(Type controlType, string path, ResourceType resourceType)
        /// <summary>
        /// 
        /// </summary>
        /// <param name="controlType"></param>
        /// <param name="path"></param>
        /// <param name="resourceType"></param>
        /// <returns></returns>
        public static bool RegisterResource(Type controlType, string path, ResourceType resourceType)
        {
            if (HttpContext.Current == null)
            {
                return false;
            }
            Page currentPage = HttpContext.Current.Handler as Page;
            if (currentPage == null)
            {
                return false;
            }
            return RegisterResource(currentPage, controlType, path, resourceType);
        }
        #endregion

        #region public static bool RegisterResource(Page page, Type controlType, string path, ResourceType resourceType)
        /// <summary>
        /// 
        /// </summary>
        /// <param name="page"></param>
        /// <param name="controlType"></param>
        /// <param name="path"></param>
        /// <param name="resourceType"></param>
        /// <returns></returns>
        public static bool RegisterResource(Page page, Type controlType, string path, ResourceType resourceType)
        {
            if (page == null)
            {
                return false;
            }

            ClientScriptManager manager = page.ClientScript;

            ScriptManager ajaxManager = ScriptManager.GetCurrent(page);

            bool useAjax = false;
            if (ajaxManager != null && ajaxManager.IsInAsyncPostBack)// || !page.IsPostBack)
            {
                useAjax = true;
            }

            //Debug.Assert(EnsureWebResourceValid(path, controlType.Assembly, true));
            //string oldPath = path;
            if (resourceType == ResourceType.Javascript && _isMinifierEnabled)
            {
                if (Debugger.IsAttached)
                {
                    if (path.EndsWith(".min.js"))
                    {
                        path = path.Substring(0, path.Length - 7) + ".js";
                    }
                }
                else if (!path.EndsWith(".min.js"))
                {
                    path = path.Substring(0, path.Length - 3) + ".min.js";
                }
            }
            //if (oldPath != path && !EnsureWebResourceValid(path, controlType.Assembly, false))
            //{
            //    path = oldPath;
            //}
            //if (oldPath != path)
            //{
            //    Debug.Assert(EnsureWebResourceValid(path, controlType.Assembly, true));
            //}
            Debug.Assert(EnsureWebResourceValid(path, controlType.Assembly, true));
            string url = page.ResolveClientUrl(manager.GetWebResourceUrl(controlType, path));

            if (ajaxManager == null || !useAjax)
            {
                if (resourceType == ResourceType.Javascript)
                {
                    if (!manager.IsClientScriptIncludeRegistered(path))
                    {
                        manager.RegisterClientScriptInclude(typeof(Page), path, url);
                        return true;
                    }
                }
                else
                {
                    if (!HeadScriptManager.IsHeaderResourceRegistered(controlType, url))
                    {
                        return HeadScriptManager.RegisterHeaderResource(page, controlType, url, resourceType);
                    }
                }
            }
            else
            {
                if (resourceType == ResourceType.Javascript)
                {
                    if (!manager.IsClientScriptBlockRegistered(path))
                    {
                        ScriptManager.RegisterClientScriptBlock(page, controlType, path, string.Format("<script src='{0}' type='text/javascript'></script>", url), false);
                        return true;
                    }
                }
                else
                {
                    bool isRegistered = HeadScriptManager.IsHeaderResourceRegistered(controlType, url);
                    if (!isRegistered)
                    {
                        isRegistered = HeadScriptManager.RegisterHeaderResource(page, controlType, url, resourceType);
                        if (useAjax && !manager.IsClientScriptBlockRegistered(path))
                        {
                            ScriptManager.RegisterClientScriptBlock(page, controlType, path,
                                //"if (window.__ExtendedControlCssLoaded == null || typeof window.__ExtendedControlCssLoaded == 'undefined') {" +
                                //"    window.__ExtendedControlCssLoaded = new Array();" +
                                //"}" +
                                //"var controlCssLoaded = window.__ExtendedControlCssLoaded; " +
                                "var head = document.getElementsByTagName('HEAD');" +
                                //"if (head && !Array.contains(controlCssLoaded,'" + url + "')) {" +
                                "if (head && head[0]) {" +
                                    "var linkElement = document.createElement('link');" +
                                    "linkElement.type = 'text/css';" +
                                    "linkElement.rel = 'stylesheet';" +
                                    "linkElement.href = '" + url + "';" +
                                    "head[0].appendChild(linkElement);" +
                                //"controlCssLoaded.push('" + url + "');" +
                                "}"
                                , true);
                            isRegistered = true;
                        }
                        return isRegistered;
                    }
                }
            }


            return false;
        }
        #endregion

        #region public static bool RegisterScript(Page page, Type controlType, string scriptWithoutTags)
        /// <summary>
        /// 
        /// </summary>
        /// <param name="page"></param>
        /// <param name="controlType"></param>
        /// <param name="scriptWithoutTags"></param>
        /// <returns></returns>
        public static bool RegisterScript(Page page, Type controlType, string scriptWithoutTags, bool registerAtTheEnd)
        {
            if (page == null || string.IsNullOrEmpty(scriptWithoutTags))
            {
                return false;
            }

            ScriptManager ajaxManager = ScriptManager.GetCurrent(page);
            string key = scriptWithoutTags.GetHashCode().ToString();
            if (ajaxManager == null)
            {
                ClientScriptManager manager = page.ClientScript;
                if (registerAtTheEnd)
                {
                    if (!manager.IsStartupScriptRegistered(key))
                    {
                        manager.RegisterStartupScript(controlType, key, scriptWithoutTags, true);
                        return true;
                    }
                }
                else if (!manager.IsClientScriptBlockRegistered(key))
                {
                    manager.RegisterClientScriptBlock(controlType, key, scriptWithoutTags, true);
                    return true;
                }
            }
            else //if (!ajaxManager.IsInAsyncPostBack)
            {
                if (registerAtTheEnd)
                {
                    ScriptManager.RegisterStartupScript(page, controlType, key, scriptWithoutTags, true);
                }
                else
                {
                    ScriptManager.RegisterClientScriptBlock(page, controlType, key, scriptWithoutTags, true);
                }
                return true;
            }
            return false;
        }
        #endregion

        #region public static bool IsScriptRegistered(Page page, string scriptWithoutTags, bool registeredAtTheEnd)
        /// <summary>
        /// 
        /// </summary>
        /// <param name="page"></param>
        /// <param name="scriptWithoutTags"></param>
        /// <param name="registeredAtTheEnd"></param>
        /// <returns>True if script is registered, otherwise false.</returns>
        public static bool IsScriptRegistered(Page page, string scriptWithoutTags, bool registeredAtTheEnd)
        {
            if (page == null || string.IsNullOrEmpty(scriptWithoutTags))
            {
                return false;
            }

            ScriptManager ajaxManager = ScriptManager.GetCurrent(page);
            string key = scriptWithoutTags.GetHashCode().ToString();
            if (ajaxManager == null)
            {
                ClientScriptManager manager = page.ClientScript;
                if (registeredAtTheEnd)
                {
                    return manager.IsStartupScriptRegistered(key);
                }
                else
                {
                    manager.IsClientScriptBlockRegistered(key);
                }
            }
            else
            {
                ReadOnlyCollection<RegisteredScript> scripts;
                if (registeredAtTheEnd)
                {
                    scripts = ajaxManager.GetRegisteredStartupScripts();
                }
                else
                {
                    scripts = ajaxManager.GetRegisteredClientScriptBlocks();
                }
                foreach (RegisteredScript script in scripts)
                {
                    if (script.Key == key)
                    {
                        return true;
                    }
                }

                return false;
            }
            return false;
        }
        #endregion

        #region public static void RegisterArrayDeclaration(Page page, string arrayName, string arrayValue)
        /// <summary>
        /// 
        /// </summary>
        /// <param name="page"></param>
        /// <param name="arrayName"></param>
        /// <param name="arrayValue"></param>
        public static void RegisterArrayDeclaration(Page page, string arrayName, string arrayValue)
        {
            ScriptManager ajaxManager = ScriptManager.GetCurrent(page);
            if (ajaxManager == null)
            {
                page.ClientScript.RegisterArrayDeclaration(arrayName, arrayValue);
            }
            else
            {
                ScriptManager.RegisterArrayDeclaration(page, arrayName, arrayValue);
            }
        }
        #endregion

        #region private static T FindControl<T>(ControlCollection controlCollection)
        /// <summary>
        /// 
        /// </summary>
        /// <param name="controlCollection"></param>
        /// <returns></returns>
        public static T FindControl<T>(this ControlCollection controlCollection)
            where T : Control
        {
            foreach (Control c in controlCollection)
            {
                if (c.GetType() == typeof(T))
                    return (T)c;
                else
                {
                    T child = FindControl<T>(c.Controls);
                    if (child != null)
                        return child;
                }
            }
            return null;
        }
        #endregion

        public static Control FindControlRecursive(this Control root, string id)
        {
            if (root.ID == id)
            {
                return root;
            }
            foreach (Control c in root.Controls)
            {
                Control t = FindControlRecursive(c, id);
                if (t != null)
                {
                    return t;
                }
            }
            return null;
        }

        #region public static T FindParent<T>(this Control control)
        /// <summary>
        /// 
        /// </summary>
        /// <param name="control"></param>
        /// <returns></returns>
        public static T FindParent<T>(this Control control)
            where T : Control
        {
            if (control.Parent is T)
            {
                return control.Parent as T;
            }
            else if (control.Parent != null)
            {
                return FindParent<T>(control.Parent);
            }
            return null;
        }
        #endregion

        #region public static List<T> GetChildren<T>(this Control control)
        /// <summary>
        /// 
        /// </summary>
        /// <param name="control"></param>
        /// <returns></returns>
        public static List<T> GetChildren<T>(this Control control)
            where T : Control
        {
            control.GetType().GetMethod("EnsureChildControls", BindingFlags.InvokeMethod | BindingFlags.Instance | BindingFlags.NonPublic).Invoke(control, null);
            List<T> lst = new List<T>();
            foreach (Control c in control.Controls)
            {
                //if (c.GetType() == typeof(T))
                if (c is T)
                    lst.Add((T)c);
                else
                {
                    List<T> childLst = GetChildren<T>(c);
                    foreach (Control child in childLst)
                    {
                        lst.Add((T)child);
                    }
                }
            }
            return lst;
        }
        #endregion

        #region public static bool EnsureWebResourceValid(string resName, Assembly asm, bool throwException)
        /// <summary>
        /// 
        /// </summary>
        /// <param name="resName"></param>
        /// <param name="asm"></param>
        /// <param name="throwException"></param>
        /// <returns></returns>
        public static bool EnsureWebResourceValid(string resName, Assembly asm, bool throwException)
        {
            if (resName.StartsWith("/") | resName.StartsWith("http://"))
            {
                //is an url
                return true;
            }
            bool bRet = EnsureManifestResourceExist(resName, asm, throwException);

            if (bRet == true)
            { //find the attribute
                bRet = false;
                // Iterate through the attributes for the assembly.
                foreach (Attribute attr in Attribute.GetCustomAttributes(asm))
                {
                    //Check for WebResource attributes.
                    if (attr.GetType() == typeof(WebResourceAttribute))
                    {
                        WebResourceAttribute wra = (WebResourceAttribute)attr;
                        Debug.WriteLine("Resource in the assembly: " + wra.WebResource.ToString() +
                          " with ContentType = " + wra.ContentType.ToString() +
                          " and PerformsSubstitution = " + wra.PerformSubstitution.ToString());
                        if (wra.WebResource == resName)
                        {
                            bRet = true;
                            break;
                        }
                    }
                }//foreach
            } //ManifestResourceExist
            if (bRet == false)
            {
                string sMsg = "Embedded resource " + resName + " in assembly " + asm.FullName + " doesn't have WebResourceAttribute ";
                if (throwException == true)
                {
                    throw new ApplicationException(sMsg);
                }
                else
                {
                    Debug.Assert(false, sMsg);
                }
            }
            return bRet;
        }
        #endregion

        #region public static bool EnsureManifestResourceExist(string resName, Assembly asm, bool throwException)
        /// <summary>
        /// if ThrowException=true, then Throw exception if resource not found
        /// </summary>
        /// <param name="resName"></param>
        /// <param name="asm"></param>
        /// <param name="throwException"></param>
        /// <returns></returns>
        public static bool EnsureManifestResourceExist(string resName, Assembly asm, bool throwException)
        {   //NOTE: If resource is located in subfolder of C# project, path of subfolder should be specified (it is included as part of namespace)
            //' Resources are named using a fully qualified name ((including namespace).
            bool bRet = true;
            ManifestResourceInfo info = asm.GetManifestResourceInfo(resName);
            if (info == null)
            {
                bRet = false;
                string sMsg = "Couldn't find embedded resource " + resName + " in assembly " + asm.FullName;
                if (throwException)
                {
                    throw new ApplicationException(sMsg);
                }
                else
                {
                    Debug.Assert(false, sMsg);
                }
            }

            return bRet;
        }
        #endregion

        #region public static string JSName(this PropertyInfo propertyInfo)
        /// <summary>
        /// 
        /// </summary>
        /// <param name="propertyInfo"></param>
        /// <returns></returns>
        public static string JSName(this PropertyInfo propertyInfo)
        {
            if (propertyInfo == null)
            {
                return string.Empty;
            }
            System.ComponentModel.AttributeCollection attributes =
                    TypeDescriptor.GetProperties(propertyInfo.DeclaringType)[propertyInfo.Name].Attributes;

            JavaScriptAttribute jsAttribute =
               (JavaScriptAttribute)attributes[typeof(JavaScriptAttribute)];

            if (jsAttribute != null)
            {
                return jsAttribute.Name;
            }
            return string.Empty;
        }
        #endregion

        #region public static string AddCssClass(string cssClass, string newClassName)
        /// <summary>
        /// 
        /// </summary>
        /// <param name="cssClass"></param>
        /// <param name="newClassName"></param>
        /// <returns></returns>
        public static string AddCssClass(string cssClass, string newClassName)
        {
            string[] cPrevNames = cssClass.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
            string[] cNewNames = newClassName.Split(new char[] { ' ', '.' }, StringSplitOptions.RemoveEmptyEntries);
            string ret = string.Empty;
            foreach (string str in cPrevNames)
            {
                ret += string.Format(" {0}", str);
            }

            List<string> lst = new List<string>(cPrevNames);
            foreach (string newClass in cNewNames)
            {
                if (!lst.Contains(newClass))
                {
                    ret += string.Format(" {0}", newClass);
                }
            }

            return ret.Trim();
        }
        #endregion

        #region public static string RemoveCssClass(string cssClass, string classNameToRemove)
        /// <summary>
        /// 
        /// </summary>
        /// <param name="cssClass"></param>
        /// <param name="classNameToRemove"></param>
        /// <returns></returns>
        public static string RemoveCssClass(string cssClass, string classNameToRemove)
        {
            string[] cPrevNames = cssClass.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
            string[] cRemovableNames = classNameToRemove.Split(new char[] { ' ', '.' }, StringSplitOptions.RemoveEmptyEntries);
            string ret = string.Empty;

            List<string> lst = new List<string>(cRemovableNames);
            foreach (string str in cPrevNames)
            {
                if (lst.Contains(str))
                {
                    continue;
                }
                ret += string.Format(" {0}", str);
            }
            return ret.Trim();
        }
        #endregion
    }

    public class ScriptComparer : IEqualityComparer<ScriptReference>
    {
        #region IEqualityComparer<ScriptReference> Members

        public bool Equals(ScriptReference x, ScriptReference y)
        {
            if (x == null || y == null)
            {
                return x == null && y == null ? true : false;
            }
            return x.ToString() == y.ToString();
        }

        public int GetHashCode(ScriptReference obj)
        {
            return obj.ToString().GetHashCode();
        }

        #endregion
    }

    #region public enum ResourceType
    /// <summary>
    /// 
    /// </summary>
    public enum ResourceType
    {
        Javascript,
        Stylesheet
    }
    #endregion
}
